package com.lost.sos.cfg;

import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.BaseColumns;
import android.util.AndroidException;
import android.util.Log;

public final class Configs {
    private static final String TAG = "Configs";
    public static final String CALL_METHOD_GET = "GET";
    public static final String CALL_METHOD_PUT = "PUT";
    public static final String AUTHORITY = "com.lost.sos"; // TODO 变成你自己的
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
    public static final String WRITE_CONFIGS = "com.lost.sos.permission.WRITE_CONFIGS"; // TODO 自定义成你自己的

    public static class SettingNotFoundException extends AndroidException {
        public SettingNotFoundException(String msg) {
            super(msg);
        }
    }

    public static class NameValueTable implements BaseColumns {
        public static final String NAME = "name";
        public static final String VALUE = "value";

        protected static boolean putString(ContentResolver resolver, Uri uri, String name, String value) {
            try {
                ContentValues values = new ContentValues();
                values.put(NAME, name);
                values.put(VALUE, value);
                resolver.insert(uri, values);
                return true;
            } catch (SQLException e) {
                Log.w(TAG, "Can't set key " + name + " in " + uri, e);
                return false;
            }
        }

        public static Uri getUriFor(Uri uri, String name) {
            return Uri.withAppendedPath(uri, name);
        }
    }

    private static class NameValueHelper {
        private final Uri mUri;
        private static final String[] SELECT_VALUE =
            new String[] { NameValueTable.VALUE };
        private static final String NAME_EQ_PLACEHOLDER = "name=?";
        private ContentProviderClient contentProviderClient = null;
        private final String mCallGetCommand;
        private final String mCallSetCommand;

        public NameValueHelper(Uri uri, String getCommand, String setCommand) {
            mUri = uri;
            mCallGetCommand = getCommand;
            mCallSetCommand = setCommand;
        }

        private ContentProviderClient lazyGetProviderClient(ContentResolver cr) {
            ContentProviderClient cpc;
            synchronized (this) {
                cpc = contentProviderClient;
                if (cpc == null) {
                    cpc = contentProviderClient = cr.acquireContentProviderClient(mUri.getAuthority());
                }
            }
            return cpc;
        }

        public boolean putString(ContentResolver cr, String name, String value) {
            try {
                Bundle arg = new Bundle();
                arg.putString(NameValueTable.VALUE, value);
                if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
                    ContentProviderClient cpc = lazyGetProviderClient(cr);
                    cpc.call(mCallSetCommand, name, arg);
                } else if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB){
                    cr.call(mUri, mCallSetCommand, name, arg);
                } else {
                    ContentValues contentValues = new ContentValues(1);
                    contentValues.put(name, value);
                    Uri uri = cr.insert(mUri, contentValues);
                    if (null == uri) {
                        throw new RemoteException();
                    }
                }
            } catch (RemoteException e) {
                Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                return false;
            }
            return true;
        }

        public String getString(ContentResolver cr, String name) {

            ContentProviderClient cpc = lazyGetProviderClient(cr);

            if (mCallGetCommand != null) {
                try {
                    Bundle args = null;
                    Bundle b = null;
                    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
                        b = cpc.call(mCallGetCommand, name, args);
                    } else if (Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
                        b = cr.call(mUri, mCallGetCommand, name, args);
                    }
                    if (b != null) {
                        String value = b.getString(NameValueTable.VALUE);
                        return value;
                    }
                    // If the response Bundle is null, we fall through
                    // to the query interface below.
                } catch (RemoteException e) {
                    // Not supported by the remote side?  Fall through
                    // to query().
                }
            }

            Cursor c = null;
            try {
                if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
                    c = cpc.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
                            new String[]{name}, null, null);
                } else {
                    c = cr.query(mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
                            new String[]{name}, null);
                }
                if (c == null) {
                    Log.w(TAG, "Can't get key " + name + " from " + mUri);
                    return null;
                }

                String value = c.moveToNext() ? c.getString(0) : null;
                return value;
            } catch (RemoteException e) {
                Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
                return null;  // Return null, but don't cache it.
            } finally {
                if (c != null) c.close();
            }


        }
    }

    public static final class Config extends NameValueTable {

        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);

        private static final NameValueHelper sNameValueHelper = new NameValueHelper(
                CONTENT_URI,
                CALL_METHOD_GET,
                CALL_METHOD_PUT);

        public static String getString(ContentResolver resolver, String name) {
            return sNameValueHelper.getString(resolver, name);
        }

        public static boolean putString(ContentResolver resolver, String name, String value) {
            return sNameValueHelper.putString(resolver, name, value);
        }

        public static Uri getUriFor(String name) {
            return getUriFor(CONTENT_URI, name);
        }

        public static int getInt(ContentResolver cr, String name, int def) {
            String v = getString(cr, name);
            try {
                return v != null ? Integer.parseInt(v) : def;
            } catch (NumberFormatException e) {
                return def;
            }
        }

        public static int getInt(ContentResolver cr, String name)
                throws SettingNotFoundException {
            String v = getString(cr, name);
            try {
                return Integer.parseInt(v);
            } catch (NumberFormatException e) {
                throw new SettingNotFoundException(name);
            }
        }

        public static boolean putInt(ContentResolver cr, String name, int value) {
            return putString(cr, name, Integer.toString(value));
        }

        public static boolean getBool(ContentResolver cr, String name, boolean def) {
            String v = getString(cr, name);
            try {
                return v != null ? Boolean.parseBoolean(v) : def;
            } catch (NumberFormatException e) {
                return def;
            }
        }

        public static boolean getBool(ContentResolver cr, String name) throws SettingNotFoundException {
            String v = getString(cr, name);
            try {
                return Boolean.parseBoolean(v);
            } catch (NumberFormatException e) {
                throw new SettingNotFoundException(name);
            }
        }

        public static boolean putBool(ContentResolver cr, String name, boolean value) {
            return putString(cr, name, Boolean.toString(value));
        }

        public static long getLong(ContentResolver cr, String name, long def) {
            String valString = getString(cr, name);
            long value;
            try {
                value = valString != null ? Long.parseLong(valString) : def;
            } catch (NumberFormatException e) {
                value = def;
            }
            return value;
        }

        public static long getLong(ContentResolver cr, String name)
                throws SettingNotFoundException {
            String valString = getString(cr, name);
            try {
                return Long.parseLong(valString);
            } catch (NumberFormatException e) {
                throw new SettingNotFoundException(name);
            }
        }

        public static boolean putLong(ContentResolver cr, String name, long value) {
            return putString(cr, name, Long.toString(value));
        }

        public static float getFloat(ContentResolver cr, String name, float def) {
            String v = getString(cr, name);
            try {
                return v != null ? Float.parseFloat(v) : def;
            } catch (NumberFormatException e) {
                return def;
            }
        }

        public static float getFloat(ContentResolver cr, String name)
                throws SettingNotFoundException {
            String v = getString(cr, name);
            if (v == null) {
                throw new SettingNotFoundException(name);
            }
            try {
                return Float.parseFloat(v);
            } catch (NumberFormatException e) {
                throw new SettingNotFoundException(name);
            }
        }

        public static boolean putFloat(ContentResolver cr, String name, float value) {
            return putString(cr, name, Float.toString(value));
        }

    }

    private static final String[] PM_WRITE_CONFIGS = {
            WRITE_CONFIGS
    };

    public static boolean isCallingPackageAllowedToWriteSettings(Context context, int uid,
            String callingPackage, boolean throwException) {
        return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, callingPackage, throwException, PM_WRITE_CONFIGS);
    }

    public static boolean checkAndNoteWriteSettingsOperation(Context context, String callingPackage, boolean throwException) {
        return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, callingPackage, throwException, PM_WRITE_CONFIGS);
    }


    public static boolean isCallingPackageAllowedToPerformAppOpsProtectedOperation(Context context, String callingPackage, boolean throwException, String[] permissions) {
        if (callingPackage == null) {
            return false;
        }

        for (String permission : permissions) {
            if (context.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                // if either of the permissions are granted, we will allow it
                return true;
            }
        }
        if (!throwException) {
            return false;
        }

        // prepare string to throw SecurityException
        StringBuilder exceptionMessage = new StringBuilder();
        exceptionMessage.append(callingPackage);
        exceptionMessage.append(" was not granted ");
        if (permissions.length > 1) {
            exceptionMessage.append(" either of these permissions: ");
        } else {
            exceptionMessage.append(" this permission: ");
        }
        for (int i = 0; i < permissions.length; i++) {
            exceptionMessage.append(permissions[i]);
            exceptionMessage.append((i == permissions.length - 1) ? "." : ", ");
        }

        throw new SecurityException(exceptionMessage.toString());
    }

    public static String getPackageNameForUid(Context context, int uid) {
        String[] packages = context.getPackageManager().getPackagesForUid(uid);
        if (packages == null) {
            return null;
        }
        return packages[0];
    }
}
